home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pico / os_unix.c < prev    next >
C/C++ Source or Header  |  1996-07-12  |  48KB  |  2,296 lines

  1. #if    !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: os_unix.c,v 4.116 1996/07/12 18:13:23 mikes Exp $";
  3. #endif
  4. /*
  5.  * Program:    Operating system dependent routines - Ultrix 4.1
  6.  *
  7.  *
  8.  * Michael Seibel
  9.  * Networks and Distributed Computing
  10.  * Computing and Communications
  11.  * University of Washington
  12.  * Administration Builiding, AG-44
  13.  * Seattle, Washington, 98195, USA
  14.  * Internet: mikes@cac.washington.edu
  15.  *
  16.  * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  17.  *
  18.  *
  19.  * Pine and Pico are registered trademarks of the University of Washington.
  20.  * No commercial use of these trademarks may be made without prior written
  21.  * permission of the University of Washington.
  22.  * 
  23.  * Pine, Pico, and Pilot software and its included text are Copyright
  24.  * 1989-1996 by the University of Washington.
  25.  * 
  26.  * The full text of our legal notices is contained in the file called
  27.  * CPYRIGHT, included with this distribution.
  28.  *
  29.  *
  30.  * Notes:
  31.  *
  32.  * - SGI IRIX 4.0.1 port by:
  33.  *       johnb@edge.cis.mcmaster.ca,  2 April 1992
  34.  *
  35.  * - Dynix/PTX port by:
  36.  *       Donn Cave, UCS/UW, 15 April 1992
  37.  *
  38.  * - 3B2, 3b1/7300, SCO ports by:
  39.  *       rll@felton.felton.ca.us, 7 Feb. 1993
  40.  *
  41.  * - Altos System V (asv) port by:
  42.  *     Tim Rice <tim@trr.metro.net>    6 Mar 96
  43.  *
  44.  * - Probably have to break this up into separate os_type.c files since
  45.  *   the #ifdef's are getting a bit cumbersome.
  46.  *
  47.  */
  48.  
  49. #include     <stdio.h>
  50. #include    <errno.h>
  51. #include    <setjmp.h>
  52. #include    <pwd.h>
  53. #if    defined(sv4) || defined(ptx)
  54. #include    <stropts.h>
  55. #include    <poll.h>
  56. #endif
  57. #if    defined(POSIX)
  58. #include    <termios.h>
  59. #if    defined(a32) || defined(a41) || defined(cvx) || defined(osf)
  60. #include    <sys/ioctl.h>
  61. #endif
  62. #else
  63. #if    defined(sv3) || defined(sgi) || defined(isc) || defined(ct) || defined(asv)
  64. #include    <termio.h>
  65. #if    defined(isc)
  66. #include    <sys/sioctl.h>
  67. #include        <sys/bsdtypes.h>
  68. #endif
  69. #else
  70. #include    <sgtty.h>
  71. #endif
  72. #endif    /* POSIX */
  73.  
  74. #include    "osdep.h"
  75. #include        "pico.h"
  76. #include    "estruct.h"
  77. #include        "edef.h"
  78. #include        "efunc.h"
  79. #include    <fcntl.h>
  80. #include    <sys/wait.h>
  81. #include    <sys/file.h>
  82. #include    <sys/types.h>
  83. #include    <sys/time.h>
  84. #if    defined(a32) || defined(a41) || defined(dpx)
  85. #include    <sys/select.h>
  86. #endif
  87.  
  88. int timeout = 0;
  89.  
  90. /*
  91.  * Immediately below are includes and declarations for the 3 basic
  92.  * terminal drivers supported; POSIX, SysVR3, and BSD
  93.  */
  94. #ifdef    POSIX
  95.  
  96. struct termios nstate,
  97.         ostate;
  98. #else
  99. #if    defined(sv3) || defined(sgi) || defined(isc) || defined(asv)
  100.  
  101. struct termio nstate,
  102.               ostate;
  103.  
  104. #else
  105. struct  sgttyb  ostate;                /* saved tty state */
  106. struct  sgttyb  nstate;                /* values for editor mode */
  107. struct  ltchars    oltchars;            /* old term special chars */
  108. struct  ltchars    nltchars = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
  109. struct  tchars    otchars;            /* old term special chars */
  110. struct  tchars    ntchars = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
  111.  
  112. #endif    /* sv3 || sgi || isc || asv */
  113. #endif    /* POSIX */
  114.  
  115. #if    defined(sv3) || defined(ct)
  116. /*
  117.  * Windowing structure to support JWINSIZE/TIOCSWINSZ/TIOCGWINSZ 
  118.  */
  119. #define ENAMETOOLONG    78
  120.  
  121. struct winsize {
  122.     unsigned short ws_row;       /* rows, in characters*/
  123.     unsigned short ws_col;       /* columns, in character */
  124.     unsigned short ws_xpixel;    /* horizontal size, pixels */
  125.     unsigned short ws_ypixel;    /* vertical size, pixels */
  126. };
  127. #endif
  128.  
  129. #if    defined(bsd) || defined(lnx)
  130. int    errno;                    /* ya, I know... */
  131. #endif
  132.  
  133. #if    defined(bsd) || defined(dyn) || defined(ct)
  134. #define    SIGTYPE int
  135. #else
  136. #define    SIGTYPE    void
  137. #endif
  138.  
  139. #ifdef    SIGCHLD
  140. static jmp_buf pico_child_state;
  141. static short   pico_child_jmp_ok, pico_child_done;
  142. #endif
  143.  
  144. #ifdef    MOUSE
  145. static int mexist = 0;        /* is the mouse driver installed? */
  146. static unsigned mnoop;
  147. #endif
  148.  
  149. #ifdef    ANSI
  150.     int      kbseq(KBESC_T *, int (*)(), int *);
  151.     SIGTYPE  do_hup_signal();
  152.     SIGTYPE  rtfrmshell();
  153. #ifdef    TIOCGWINSZ
  154.     SIGTYPE  winch_handler();
  155. #endif
  156. #ifdef    SIGCHLD
  157.     SIGTYPE  child_handler();
  158. #endif
  159.  
  160. #else
  161.     int      kbseq();
  162.     SIGTYPE  do_hup_signal();
  163.     SIGTYPE  rtfrmshell();
  164. #ifdef    TIOCGWINSZ
  165.     SIGTYPE  winch_handler();
  166. #endif
  167. #ifdef    SIGCHLD
  168.     SIGTYPE  child_handler();
  169. #endif
  170.  
  171. #endif    /* ANSI */
  172.  
  173.  
  174. /*
  175.  * for alt_editor arg[] building
  176.  */
  177. #define    MAXARGS    10
  178.  
  179. /*
  180.  * ttopen - this function is called once to set up the terminal device 
  181.  *          streams.  if called as pine composer, don't mess with
  182.  *          tty modes, but set signal handlers.
  183.  */
  184. ttopen()
  185. {
  186.     if(Pmaster == NULL){
  187. #ifdef    POSIX
  188.     tcgetattr (0, &ostate);
  189.     tcgetattr (0, &nstate);
  190.     nstate.c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN);
  191.     nstate.c_iflag &= ~ICRNL;
  192.     nstate.c_oflag &= ~(ONLCR | OPOST);
  193.     nstate.c_cc[VMIN] = 1;
  194.     nstate.c_cc[VTIME] = 0;
  195.     tcsetattr (0, TCSADRAIN, &nstate);
  196. #else
  197. #if    defined(sv3) || defined(sgi) || defined(isc) || defined(ct) || defined(asv)
  198.     (void) ioctl(0, TCGETA, &ostate);
  199.     (void) ioctl(0, TCGETA, &nstate);    /** again! **/
  200.  
  201.     nstate.c_lflag &= ~(ICANON | ISIG | ECHO);    /* noecho raw mode  */
  202.     nstate.c_oflag &= ~(OPOST | ONLCR);
  203.     nstate.c_iflag &= ~ICRNL;
  204.         
  205.     nstate.c_cc[VMIN] = '\01';  /* minimum # of chars to queue  */
  206.     nstate.c_cc[VTIME] = '\0'; /* minimum time to wait for input */
  207.     (void) ioctl(0, TCSETA, &nstate);
  208. #else
  209.     ioctl(0, TIOCGETP, &ostate);        /* save old state */
  210.     ioctl(0, TIOCGLTC, &oltchars);        /* Save old lcharacters */
  211.     ioctl(0, TIOCGETC, &otchars);        /* Save old characters */
  212.     ioctl(0, TIOCGETP, &nstate);        /* get base of new state */
  213.     nstate.sg_flags |= RAW;
  214.     nstate.sg_flags &= ~(ECHO|CRMOD);    /* no echo for now... */
  215.     ioctl(0, TIOCSETP, &nstate);        /* set mode */
  216.  
  217.     ioctl(0, TIOCSLTC, &nltchars);        /* put new lcharacter into K */
  218.     ioctl(0, TIOCSETC, &ntchars);        /* put new character into K */
  219. #endif    /* sv3 */
  220. #endif    /* POSIX */
  221. #ifdef    MOUSE
  222.     if(gmode & MDMOUSE)
  223.       init_mouse();
  224. #endif    /* MOUSE */
  225.     }
  226.  
  227. #ifdef    MOUSE
  228.     if(mexist)
  229.       kpinsert(&pico_kbesc, "\033[M", K_XTERM_MOUSE);
  230. #endif    /* MOUSE */
  231.  
  232.     if(!Pmaster || (gmode ^ MDEXTFB))
  233.       picosigs();
  234.  
  235.     return(1);
  236. }
  237.  
  238.  
  239. /*
  240.  * ttresize - recompute the screen dimensions if necessary, and then
  241.  *          adjust pico's internal buffers accordingly.
  242.  */
  243. int
  244. ttresize ()
  245. {
  246.     int row = -1, col = -1;
  247.  
  248.     ttgetwinsz(&row, &col);
  249.     resize_pico(row, col);
  250. }
  251.  
  252.  
  253. /*
  254.  * picosigs - Install any handlers for the signals we're interested
  255.  *          in catching.
  256.  */
  257. picosigs()
  258. {
  259.     signal(SIGHUP,  do_hup_signal);    /* deal with SIGHUP */
  260.     signal(SIGTERM, do_hup_signal);    /* deal with SIGTERM */
  261. #ifdef    SIGTSTP
  262.     signal(SIGTSTP, SIG_DFL);
  263. #endif
  264. #ifdef    TIOCGWINSZ
  265.     signal(SIGWINCH, winch_handler); /* window size changes */
  266. #endif
  267. }
  268.  
  269.  
  270. /*
  271.  * ttclose - this function gets called just before we go back home to 
  272.  *           the command interpreter.  If called as pine composer, don't
  273.  *           worry about modes, but set signals to default, pine will 
  274.  *           rewire things as needed.
  275.  */
  276. ttclose()
  277. {
  278.     if(Pmaster){
  279.     signal(SIGHUP, SIG_DFL);
  280. #ifdef    SIGCONT
  281.     signal(SIGCONT, SIG_DFL);
  282. #endif
  283. #ifdef    TIOCGWINSZ
  284.     signal(SIGWINCH, SIG_DFL);
  285. #endif
  286.     }
  287.     else{
  288. #ifdef    POSIX
  289.     tcsetattr (0, TCSADRAIN, &ostate);
  290. #else
  291. #if    defined(sv3) || defined(sgi) || defined(isc) || defined(ct) || defined(asv)
  292.         ioctl(0, TCSETA, &ostate);
  293. #else
  294.     ioctl(0, TIOCSETP, &ostate);
  295.     ioctl(0, TIOCSLTC, &oltchars);
  296.     ioctl(0, TIOCSETC, &otchars);
  297.  
  298.     /*
  299.      * This works around a really weird problem.  On slow speed lines,
  300.      * if an exit happens with some number of characters still to be
  301.      * written in the terminal driver, one or more characters will 
  302.      * be changed when they finally get drained.  This can be reproduced
  303.      * on a 2400bps line, writing a multi-line buffer on exit using
  304.      * a vt100 type terminal.  It turns out the last char in the
  305.      * escape sequence turning off reverse video was getting changed
  306.      * from 'm' to ' '.  I said it was weird.
  307.      */
  308.     if(ostate.sg_ospeed <= B2400)
  309.       sleep(1);
  310. #endif
  311. #endif    /* POSIX */
  312. #ifdef    MOUSE
  313.     end_mouse();
  314. #endif
  315.     }
  316.  
  317.     return(1);
  318. }
  319.  
  320.  
  321. /*
  322.  * ttspeed - return TRUE if tty line speed < 9600 else return FALSE
  323.  */
  324. ttisslow()
  325. {
  326. #if    defined(POSIX)
  327.     struct termios tty;
  328.  
  329.     return((tcgetattr (1, &tty) == 0) ? cfgetospeed (&tty) < B9600 : FALSE);
  330. #else
  331. #if    defined(sv3) || defined(sgi) || defined(isc) || defined(asv)
  332.     struct termio tty;
  333.  
  334. #ifdef asv
  335.     return((ioctl(1, TCGETA, &tty) == 0) ? (tty.c_cflag && CBAUD) < B9600 : FALSE);
  336. #else /* asv */
  337.     return((tcgetattr (1, &tty) == 0) ? cfgetospeed (&tty) < B9600 : FALSE);
  338. #endif /* asv */
  339. #else
  340.     struct  sgttyb tty;
  341.  
  342.     return((ioctl(1, TIOCGETP, &tty) == 0) ? tty.sg_ospeed < B9600 : FALSE);
  343. #endif
  344. #endif
  345. }
  346.  
  347.  
  348. /*
  349.  * ttgetwinsz - set global row and column values (if we can get them)
  350.  *        and return.
  351.  */
  352. ttgetwinsz(row, col)
  353.     int *row, *col;
  354. {
  355.     if(*row < 0)
  356.       *row = NROW - 1;
  357.     if(*col <= 0)
  358.       *col = NCOL;
  359. #ifdef TIOCGWINSZ
  360.     {
  361.     struct winsize win;
  362.  
  363.     if (ioctl(0, TIOCGWINSZ, &win) == 0) {    /* set to anything useful.. */
  364.         if(win.ws_row)            /* ... the tty drivers says */
  365.           *row = win.ws_row - 1;
  366.  
  367.         if(win.ws_col)
  368.           *col = win.ws_col;
  369.     }
  370.  
  371.     signal(SIGWINCH, winch_handler);    /* window size changes */
  372.     }
  373. #endif
  374. }
  375.  
  376.  
  377. /*
  378.  * ttputc - Write a character to the display. 
  379.  */
  380. ttputc(c)
  381. {
  382.     return(putc(c, stdout));
  383. }
  384.  
  385.  
  386. /*
  387.  * ttflush - flush terminal buffer. Does real work where the terminal 
  388.  *           output is buffered up. A no-operation on systems where byte 
  389.  *           at a time terminal I/O is done.
  390.  */
  391. ttflush()
  392. {
  393.     return(fflush(stdout));
  394. }
  395.  
  396.  
  397. /*
  398.  * ttgetc - Read a character from the terminal, performing no editing 
  399.  *          and doing no echo at all.
  400.  */
  401. ttgetc()
  402. {
  403.     unsigned char c;
  404.     int i;
  405.  
  406.     if((i = read(0, &c, 1)) == 1)            /* success */
  407.       return((int) c);
  408.     else if(i == 0 || errno != EINTR)
  409.       kill(getpid(), SIGHUP);                /* eof or bad error */
  410.  
  411.     return(NODATA);                    /* acceptable error */
  412. }
  413.  
  414.  
  415. #if    TYPEAH
  416. /* 
  417.  * typahead - Check to see if any characters are already in the
  418.  *          keyboard buffer
  419.  */
  420. typahead()
  421. {
  422.     int x;    /* holds # of pending chars */
  423.  
  424.     return((ioctl(0,FIONREAD,&x) < 0) ? 0 : x);
  425. }
  426. #endif
  427.  
  428.  
  429. /*
  430.  * ReadyForKey - return true if there's no timeout or we're told input
  431.  *         is available...
  432.  */
  433. ReadyForKey(timeout)
  434.     int timeout;
  435. {
  436.     if(timeout > 0){
  437.     /*
  438.      * simple use of select/poll here to handle requested timeouts
  439.      * while waiting for keyboard input...
  440.      */
  441. #if    defined(ptx) || defined(sv4)
  442.     struct pollfd pollfd;
  443.     int    rv;
  444.  
  445.     pollfd.fd     = 0;
  446.     pollfd.events = POLLIN;
  447.     while((rv = poll(&pollfd, 1, timeout * 1000)) < 0 && errno == EAGAIN)
  448.       ;
  449. #else
  450.     struct timeval ts;
  451.     fd_set readfds;
  452.     int    rv;
  453.  
  454.     FD_ZERO(&readfds);        /* blank out all bits */
  455.     FD_SET(0, &readfds);        /* set stdin's bit */
  456.     ts.tv_sec  = timeout;        /* set the timeout */
  457.     ts.tv_usec = 0;
  458.  
  459.     rv = select(1, &readfds, 0, &readfds, &ts); /* read stdin */
  460. #endif
  461.     if(rv < 0){
  462.         if(errno == EINTR){        /* interrupted? */
  463.         return(0);        /* return like we timed out */
  464.         }
  465.         else{
  466.         emlwrite("\007Problem reading from keyboard!", NULL);
  467.         kill(getpid(), SIGHUP);    /* Bomb out (saving our work)! */
  468.         }
  469.     }
  470.     else if(rv == 0)
  471.       return(0);            /* we really did time out */
  472.     }
  473.  
  474.     return(1);
  475. }
  476.  
  477.  
  478. /*
  479.  * GetKey - Read in a key.
  480.  * Do the standard keyboard preprocessing. Convert the keys to the internal
  481.  * character set.  Resolves escape sequences and returns no-op if global
  482.  * timeout value exceeded.
  483.  */
  484. GetKey()
  485. {
  486.     int    c, status, cc;
  487.  
  488.     if(!ReadyForKey(timeout))
  489.       return(NODATA);
  490.  
  491.     switch(status = kbseq(pico_kbesc, term.t_getchar, &c)){
  492.       case 0:     /* regular character */
  493.     break;
  494.  
  495.       case K_DOUBLE_ESC:
  496.     c = (*term.t_getchar)();
  497.  
  498.     if(isdigit((unsigned char)c)){
  499.         int n = 0, i = c - '0';
  500.  
  501.         if(!strchr("012", c))
  502.           return(c);        /* bogus literal char value */
  503.  
  504.         while(n++ < 2){
  505.         c = (*term.t_getchar)();
  506.                     /* remember Horner? */
  507.         if(!isdigit((unsigned char)c) || (n == 1 && i == 2 && c > '5'))
  508.           return(c);        /* bogus literal char value */
  509.  
  510.         i = (i * 10) + (c - '0');
  511.         }
  512.  
  513.         c = i;
  514.         break;
  515.     }
  516.     else{
  517.         if(islower((unsigned char)c))    /* canonicalize c */
  518.           c = toupper((unsigned char)c);
  519.  
  520.         return((isalpha((unsigned char)c) || c == '@'
  521.             || (c >= '[' && c <= '_'))
  522.             ? (CTRL | c) : ((c == ' ') ? (CTRL | '@') : c));
  523.     }
  524.  
  525. #ifdef MOUSE
  526.       case K_XTERM_MOUSE:
  527.     {
  528.         static int down = 0;
  529.         int        x, y, button;
  530.         unsigned   ch;
  531.  
  532.         button = (*term.t_getchar)() & 0x03;
  533.         c = (*term.t_getchar)();
  534.         x = c - '!';
  535.         c = (*term.t_getchar)();
  536.         y = c - '!';
  537.         if(button == 0){
  538.         down = 1;
  539.         if(checkmouse(&ch, 1, x, y))
  540.           return(ch);
  541.         }
  542.         else if(down && button == 3){
  543.         down = 0;
  544.         if(checkmouse(&ch, 0, x, y))
  545.           return(ch);
  546.         }
  547.  
  548.         return(NODATA);
  549.     }
  550.  
  551.     break;
  552. #endif /* MOUSE */
  553.  
  554.       case  K_PAD_UP        :
  555.       case  K_PAD_DOWN        :
  556.       case  K_PAD_RIGHT        :
  557.       case  K_PAD_LEFT        :
  558.       case  K_PAD_PREVPAGE    :
  559.       case  K_PAD_NEXTPAGE    :
  560.       case  K_PAD_HOME        :
  561.       case  K_PAD_END        :
  562.       case  K_PAD_DELETE    :
  563.       case F1  :
  564.       case F2  :
  565.       case F3  :
  566.       case F4  :
  567.       case F5  :
  568.       case F6  :
  569.       case F7  :
  570.       case F8  :
  571.       case F9  :
  572.       case F10 :
  573.       case F11 :
  574.       case F12 :
  575.     return(status);
  576.  
  577.       case K_SWALLOW_TIL_Z:
  578.     status = BADESC;
  579.       case K_SWALLOW_UP:
  580.       case K_SWALLOW_DOWN:
  581.       case K_SWALLOW_LEFT:
  582.       case K_SWALLOW_RIGHT:
  583.     do
  584.       if(!ReadyForKey(2)){
  585.           status = BADESC;
  586.           break;
  587.       }
  588.     while(!strchr("~qz", (*term.t_getchar)()));
  589.  
  590.     return((status == BADESC)
  591.         ? status
  592.         : status - (K_SWALLOW_UP - K_PAD_UP));
  593.     break;
  594.  
  595.       case K_KERMIT:
  596.     for(cc = 0; cc != '\033' || ((*term.t_getchar)() & 0x7f) != '\\';)
  597.       cc = (*term.t_getchar)() & 0x7f;
  598.  
  599.     c = NODATA;
  600.     break;
  601.  
  602.       case BADESC:
  603.     (*term.t_beep)();
  604.     return(status);
  605.  
  606.       default:                /* punt the whole thing    */
  607.     (*term.t_beep)();
  608.     break;
  609.     }
  610.  
  611.     if (c>=0x00 && c<=0x1F)                 /* C0 control -> C-     */
  612.       c = CTRL | (c+'@');
  613.  
  614.     return (c);
  615. }
  616.  
  617.  
  618.  
  619. /* 
  620.  * kbseq - looks at an escape sequence coming from the keyboard and 
  621.  *         compares it to a trie of known keyboard escape sequences, and
  622.  *         performs the function bound to the escape sequence.
  623.  * 
  624.  *         returns: BADESC, the escaped function, or 0 if a regular char.
  625.  */
  626. kbseq(trie, getcfunc, c)
  627.     KBESC_T *trie;
  628.     int    (*getcfunc)();
  629.     int        *c;
  630. {
  631.     register char     b;
  632.     register int      first = 1;
  633.     register KBESC_T *current = trie;
  634.  
  635.     if(trie == NULL)                /* bag it */
  636.       return(BADESC);
  637.  
  638.     while(1){
  639.     b = *c = (*getcfunc)();
  640.  
  641.     while(current->value != b){
  642.         if(current->left == NULL){        /* NO MATCH */
  643.         if(first)
  644.           return(0);            /* regular char */
  645.         else
  646.           return(BADESC);
  647.         }
  648.         current = current->left;
  649.     }
  650.  
  651.     if(current->down == NULL)        /* match!!!*/
  652.       return(current->func);
  653.     else
  654.       current = current->down;
  655.  
  656.     first = 0;
  657.     }
  658. }
  659.  
  660.  
  661.  
  662. #define    newnode()    (KBESC_T *)malloc(sizeof(KBESC_T))
  663. /*
  664.  * kpinsert - insert a keystroke escape sequence into the global search
  665.  *          structure.
  666.  */
  667. void
  668. kpinsert(trie, kstr, kval)
  669.     KBESC_T **trie;
  670.     char     *kstr;
  671.     int       kval;
  672. {
  673.     register    char    *buf;
  674.     register    KBESC_T *temp;
  675.     register    KBESC_T *trail;
  676.  
  677.     if(kstr == NULL)
  678.       return;
  679.  
  680. #ifndef TERMCAP_WINS
  681.     /*
  682.      * Don't allow escape sequences that don't start with ESC unless
  683.      * TERMCAP_WINS is defined.  This is to protect against mistakes
  684.      * in termcap files.
  685.      */
  686.     if(*kstr != '\033')
  687.       return;
  688. #endif /* !TERMCAP_WINS */
  689.  
  690.     temp = trail = *trie;
  691.     buf = kstr;
  692.  
  693.     for(;;){
  694.     if(temp == NULL){
  695.         temp = newnode();
  696.         temp->value = *buf;
  697.         temp->func = 0;
  698.         temp->left = NULL;
  699.         temp->down = NULL;
  700.         if(*trie == NULL)
  701.           *trie = temp;
  702.         else
  703.           trail->down = temp;
  704.     }
  705.     else{                /* first entry */
  706.         while((temp != NULL) && (temp->value != *buf)){
  707.         trail = temp;
  708.         temp = temp->left;
  709.         }
  710.  
  711.         if(temp == NULL){   /* add new val */
  712.         temp = newnode();
  713.         temp->value = *buf;
  714.         temp->func = 0;
  715.         temp->left = NULL;
  716.         temp->down = NULL;
  717.         trail->left = temp;
  718.         }
  719.     }
  720.  
  721.     if(*(++buf) == '\0')
  722.       break;
  723.     else{
  724.         /*
  725.          * Ignore attempt to overwrite shorter existing escape sequence.
  726.          * That means that sequences with higher priority should be
  727.          * set up first, so if we want termcap sequences to override
  728.          * hardwired sequences, put the kpinsert calls for the
  729.          * termcap sequences first.  (That's what you get if you define
  730.          * TERMCAP_WINS.)
  731.          */
  732.         if(temp->func != 0)
  733.           return;
  734.  
  735.         trail = temp;
  736.         temp = temp->down;
  737.     }
  738.     }
  739.     
  740.     /*
  741.      * Ignore attempt to overwrite longer sequences we are a prefix
  742.      * of (down != NULL) and exact same sequence (func != 0).
  743.      */
  744.     if(temp != NULL && temp->down == NULL && temp->func == 0)
  745.       temp->func = kval;
  746. }
  747.  
  748.  
  749.  
  750. /*
  751.  * kbdestroy() - kills the key pad function key search tree
  752.  *         and frees all lines associated with it
  753.  */
  754. void
  755. kbdestroy(kb)
  756.     KBESC_T *kb;
  757. {
  758.     if(kb){
  759.     kbdestroy(kb->left);
  760.     kbdestroy(kb->down);
  761.     free((char *) kb);
  762.     }
  763. }
  764.  
  765.  
  766.  
  767. /*
  768.  * alt_editor - fork off an alternate editor for mail message composition 
  769.  *              if one is configured and passed from pine.  If not, only
  770.  *              ask for the editor if advanced user flag is set, and 
  771.  *              suggest environment's EDITOR value as default.
  772.  */
  773. alt_editor(f, n)
  774. {
  775.     char   eb[NLINE];                /* buf holding edit command */
  776.     char   *fn;                    /* tmp holder for file name */
  777.     char   *cp;
  778.     char   *args[MAXARGS];            /* ptrs into edit command */
  779.     char   result[128];                /* result string */
  780.     int       child, pid, i, done = 0;
  781.     long   l;
  782.     FILE   *p;
  783.     SIGTYPE (*ohup)(), (*oint)(), (*osize)(), (*ostop)(), (*ostart)();
  784. #if    defined(POSIX) || defined(sv3) || defined(COHERENT) || defined(isc) || defined(neb)
  785.     int    stat;
  786. #ifndef    WIFEXITED
  787. #define    WIFEXITED(X)    (!((X) & 0xff))        /* low bits, child killed */
  788. #endif
  789. #ifndef    WEXITSTATUS
  790. #define    WEXITSTATUS(X)    ((X) >> 8)        /* high bits, exit value */
  791. #endif
  792. #else
  793.     union  wait stat;
  794. #ifndef    WIFEXITED
  795. #define    WIFEXITED(X)    (!(X).w_termsig)    /* nonzero if child killed */
  796. #endif
  797. #ifndef    WEXITSTATUS
  798. #define    WEXITSTATUS(X)    X.w_retcode        /* child's exit value */
  799. #endif
  800. #endif
  801.  
  802.     if(Pmaster == NULL)
  803.       return(-1);
  804.  
  805.     if(gmode&MDSCUR){
  806.     emlwrite("Alternate %s not available in restricted mode",
  807.          f ? "speller" : "editor");
  808.     return(-1);
  809.     }
  810.  
  811.     strcpy(result, "Alternate %s complete.");
  812.  
  813.     if(f){
  814.     if(alt_speller)
  815.       strcpy(eb, alt_speller);
  816.     else
  817.       return(-1);
  818.     }
  819.     else{
  820.     if(Pmaster->alt_ed == NULL){
  821.         if(!(gmode&MDADVN)){
  822.         emlwrite("\007Unknown Command",NULL);
  823.         return(-1);
  824.         }
  825.  
  826.         if(getenv("EDITOR"))
  827.           strcpy(eb, (char *)getenv("EDITOR"));
  828.         else
  829.           *eb = '\0';
  830.  
  831.         while(!done){
  832.         pid = mlreplyd("Which alternate editor ? ", eb,
  833.                    NLINE, QDEFLT, NULL);
  834.         switch(pid){
  835.           case ABORT:
  836.             return(-1);
  837.           case HELPCH:
  838.             emlwrite("no alternate editor help yet", NULL);
  839.  
  840. /* take sleep and break out after there's help */
  841.             sleep(3);
  842.             break;
  843.           case (CTRL|'L'):
  844.             sgarbf = TRUE;
  845.             update();
  846.             break;
  847.           case TRUE:
  848.           case FALSE:            /* does editor exist ? */
  849.             if(*eb == '\0'){        /* leave silently? */
  850.             mlerase();
  851.             return(-1);
  852.             }
  853.  
  854.             done++;
  855.             break;
  856.             default:
  857.             break;
  858.         }
  859.         }
  860.     }
  861.     else
  862.       strcpy(eb, Pmaster->alt_ed);
  863.     }
  864.  
  865.     if((fn=writetmp(0, 1)) == NULL){        /* get temp file */
  866.     emlwrite("Problem writing temp file for alt editor", NULL);
  867.     return(-1);
  868.     }
  869.  
  870.     strcat(eb, " ");
  871.     strcat(eb, fn);
  872.  
  873.     cp = eb;
  874.     for(i=0; *cp != '\0';i++){            /* build args array */
  875.     if(i < MAXARGS){
  876.         args[i] = NULL;            /* in case we break out */
  877.     }
  878.     else{
  879.         emlwrite("Too many args for command!", NULL);
  880.         return(-1);
  881.     }
  882.  
  883.     while(isspace((unsigned char)(*cp)))
  884.       if(*cp != '\0')
  885.         cp++;
  886.       else
  887.         break;
  888.  
  889.     args[i] = cp;
  890.  
  891.     while(!isspace((unsigned char)(*cp)))
  892.       if(*cp != '\0')
  893.         cp++;
  894.       else
  895.         break;
  896.  
  897.     if(*cp != '\0')
  898.       *cp++ = '\0';
  899.     }
  900.  
  901.     args[i] = NULL;
  902.  
  903.     for(i = 0; i <= ((Pmaster) ? term.t_nrow : term.t_nrow - 1); i++){
  904.     movecursor(i, 0);
  905.     if(!i){
  906.         fputs("Invoking alternate ", stdout);
  907.         fputs(f ? "speller..." : "editor...", stdout);
  908.     }
  909.  
  910.     peeol();
  911.     }
  912.  
  913.     (*term.t_flush)();
  914.     if(Pmaster)
  915.       (*Pmaster->raw_io)(0);            /* turn OFF raw mode */
  916.  
  917. #ifdef    SIGCHLD
  918.     if(Pmaster){
  919.     /*
  920.      * The idea here is to keep any mail connections our caller
  921.      * may have open while our child's out running around...
  922.      */
  923.     pico_child_done = pico_child_jmp_ok = 0;
  924.     (void) signal(SIGCHLD,  child_handler);
  925.     }
  926. #endif
  927.  
  928.     if((child = fork()) > 0){        /* wait for the child to finish */
  929.     ohup = signal(SIGHUP, SIG_IGN);    /* ignore signals for now */
  930.     oint = signal(SIGINT, SIG_IGN);
  931. #ifdef    TIOCGWINSZ
  932.         osize = signal(SIGWINCH, SIG_IGN);
  933. #endif
  934.  
  935. #ifdef    SIGCHLD
  936.     if(Pmaster){
  937.         while(!pico_child_done){
  938.         (*Pmaster->newmail)(0, 0);
  939.         if(!pico_child_done)
  940.           if(setjmp(pico_child_state) == 0){
  941.               pico_child_jmp_ok = 1;
  942.               sleep(600);
  943.           }
  944.  
  945.         pico_child_jmp_ok = 0;
  946.         }
  947.     }
  948. #endif
  949.  
  950.     while((pid = (int) wait(&stat)) != child)
  951.       ;
  952.  
  953.     signal(SIGHUP, ohup);    /* restore signals */
  954.     signal(SIGINT, oint);
  955. #ifdef    TIOCGWINSZ
  956.         signal(SIGWINCH, osize);
  957. #endif
  958.  
  959.     /*
  960.      * Report child's unnatural or unhappy exit...
  961.      */
  962.     if(WIFEXITED(stat) && WEXITSTATUS(stat) == 0)
  963.       strcpy(result, "Alternate %s done");
  964.     else
  965.       sprintf(result, "Alternate %%s abnormally terminated (%d)",
  966.           WIFEXITED(stat) ? WEXITSTATUS(stat) : -1);
  967.     }
  968.     else if(child == 0){        /* spawn editor */
  969.     signal(SIGHUP, SIG_DFL);    /* let editor handle signals */
  970.     signal(SIGINT, SIG_DFL);
  971. #ifdef    TIOCGWINSZ
  972.         signal(SIGWINCH, SIG_DFL);
  973. #endif
  974. #ifdef    SIGCHLD
  975.     (void) signal(SIGCHLD,  SIG_DFL);
  976. #endif
  977.     if(execvp(args[0], args) < 0)
  978.       exit(-1);
  979.     }
  980.     else                /* error! */
  981.       sprintf(result, "\007Can't fork %%s: %s", errstr(errno));
  982.  
  983. #ifdef    SIGCHLD
  984.     (void) signal(SIGCHLD,  SIG_DFL);
  985. #endif
  986.  
  987.     if(Pmaster)
  988.       (*Pmaster->raw_io)(1);        /* turn ON raw mode */
  989.  
  990.     /*
  991.      * replace edited text with new text 
  992.      */
  993.     curbp->b_flag &= ~BFCHG;        /* make sure old text gets blasted */
  994.     readin(fn, 0);            /* read new text overwriting old */
  995.     unlink(fn);                /* blast temp file */
  996.     curbp->b_flag |= BFCHG;        /* mark dirty for packbuf() */
  997.     ttopen();                /* reset the signals */
  998.     refresh(0, 1);            /* redraw */
  999.     update();
  1000.     emlwrite(result, f ? "speller" : "editor");
  1001.     return(0);
  1002. }
  1003.  
  1004.  
  1005.  
  1006. /*
  1007.  *  bktoshell - suspend and wait to be woken up
  1008.  */
  1009. bktoshell()        /* suspend MicroEMACS and wait to wake up */
  1010. {
  1011. #ifdef    SIGTSTP
  1012.     int pid;
  1013.  
  1014.     if(!(gmode&MDSSPD)){
  1015.     emlwrite("\007Unknown command: ^Z", NULL);
  1016.     return;
  1017.     }
  1018.  
  1019.     if(gmode&MDSPWN){
  1020.     char *shell;
  1021.  
  1022.     if(Pmaster){
  1023.         (*Pmaster->raw_io)(0);    /* actually in pine source */
  1024.  
  1025.         movecursor(0, 0);
  1026.         (*term.t_eeop)();
  1027.         printf("\n\n\nUse \"exit\" to return to Pine\n");
  1028.     }
  1029.     else{
  1030.         vttidy();
  1031.         movecursor(0, 0);
  1032.         (*term.t_eeop)();
  1033.         printf("\n\n\nUse \"exit\" to return to Pi%s\n",
  1034.            (gmode & MDBRONLY) ? "lot" : "co");
  1035.     }
  1036.  
  1037.     system((shell = (char *)getenv("SHELL")) ? shell : "/bin/csh");
  1038.     rtfrmshell();            /* fixup tty */
  1039.     }
  1040.     else {
  1041.     if(Pmaster){
  1042.         (*Pmaster->raw_io)(0);    /* actually in pine source */
  1043.  
  1044.         movecursor(term.t_nrow, 0);
  1045.         printf("\n\n\nUse \"fg\" to return to Pine\n");
  1046.     }
  1047.     else{
  1048.         movecursor(term.t_nrow-1, 0);
  1049.         peeol();
  1050.         movecursor(term.t_nrow, 0);
  1051.         peeol();
  1052.         movecursor(term.t_nrow, 0);
  1053.         printf("\n\n\nUse \"fg\" to return to Pi%s\n",
  1054.            (gmode & MDBRONLY) ? "lot" : "co");
  1055.         ttclose();
  1056.     }
  1057.  
  1058.     movecursor(term.t_nrow, 0);
  1059.     peeol();
  1060.     (*term.t_flush)();
  1061.  
  1062.     signal(SIGCONT, rtfrmshell);    /* prepare to restart */
  1063.     signal(SIGTSTP, SIG_DFL);            /* prepare to stop */
  1064.     kill(0, SIGTSTP);
  1065.     }
  1066. #endif
  1067. }
  1068.  
  1069.  
  1070. /* 
  1071.  * rtfrmshell - back from shell, fix modes and return
  1072.  */
  1073. SIGTYPE
  1074. rtfrmshell()
  1075. {
  1076. #ifdef    SIGCONT
  1077.     signal(SIGCONT, SIG_DFL);
  1078.  
  1079.     if(Pmaster){
  1080.     (*Pmaster->raw_io)(1);            /* actually in pine source */
  1081.     (*Pmaster->keybinit)(gmode&MDFKEY);    /* using f-keys? */
  1082.     }
  1083.  
  1084.     ttopen();
  1085.     ttresize();
  1086.     pclear(0, term.t_nrow);
  1087.     refresh(0, 1);
  1088. #endif
  1089. }
  1090.  
  1091.  
  1092.  
  1093. /*
  1094.  * do_hup_signal - jump back in the stack to where we can handle this
  1095.  */
  1096. SIGTYPE
  1097. do_hup_signal()
  1098. {
  1099.     signal(SIGHUP,  SIG_IGN);            /* ignore further SIGHUP's */
  1100.     signal(SIGTERM, SIG_IGN);            /* ignore further SIGTERM's */
  1101.     if(Pmaster){
  1102.     extern jmp_buf finstate;
  1103.  
  1104.     longjmp(finstate, COMP_GOTHUP);
  1105.     }
  1106.     else{
  1107.     /*
  1108.      * if we've been interrupted and the buffer is changed,
  1109.      * save it...
  1110.      */
  1111.     if(anycb() == TRUE){            /* time to save */
  1112.         if(curbp->b_fname[0] == '\0'){    /* name it */
  1113.         strcpy(curbp->b_fname, "pico.save");
  1114.         }
  1115.         else{
  1116.         strcat(curbp->b_fname, ".save");
  1117.         }
  1118.         writeout(curbp->b_fname);
  1119.     }
  1120.     vttidy();
  1121.     exit(1);
  1122.     }
  1123. }
  1124.  
  1125.  
  1126. /*
  1127.  * big bitmap of ASCII characters allowed in a file name
  1128.  * (needs reworking for other char sets)
  1129.  */
  1130. unsigned char okinfname[32] = {
  1131.       0,    0,             /* ^@ - ^G, ^H - ^O  */
  1132.       0,    0,            /* ^P - ^W, ^X - ^_  */
  1133.       0x80, 0x17,        /* SP - ' ,  ( - /   */
  1134.       0xff, 0xc4,        /*  0 - 7 ,  8 - ?   */
  1135.       0x7f, 0xff,        /*  @ - G ,  H - O   */
  1136.       0xff, 0xe1,        /*  P - W ,  X - _   */
  1137.       0x7f, 0xff,        /*  ` - g ,  h - o   */
  1138.       0xff, 0xf6,        /*  p - w ,  x - DEL */
  1139.       0,    0,             /*  > DEL   */
  1140.       0,    0,            /*  > DEL   */
  1141.       0,    0,             /*  > DEL   */
  1142.       0,    0,             /*  > DEL   */
  1143.       0,    0             /*  > DEL   */
  1144. };
  1145.  
  1146.  
  1147. /*
  1148.  * fallowc - returns TRUE if c is allowable in filenames, FALSE otw
  1149.  */
  1150. fallowc(c)
  1151. char c;
  1152. {
  1153.     return(okinfname[c>>3] & 0x80>>(c&7));
  1154. }
  1155.  
  1156.  
  1157. /*
  1158.  * fexist - returns TRUE if the file exists with mode passed in m, 
  1159.  *          FALSE otherwise.  By side effect returns length of file in l
  1160.  */
  1161. fexist(file, m, l)
  1162. char *file;
  1163. char *m;            /* files mode: r,w,rw,t or x */
  1164. long *l;            /* t means use lstat         */
  1165. {
  1166.     struct stat    sbuf;
  1167.     extern int lstat();
  1168.     int        (*stat_f)() = (m && *m == 't') ? lstat : stat;
  1169.     int accessible;
  1170. /* define them here so we don't have to worry about what to include */
  1171. #define EXECUTE_ACCESS  01
  1172. #define WRITE_ACCESS    02
  1173. #define READ_ACCESS     04
  1174.  
  1175.     if(l)
  1176.       *l = 0L;
  1177.     
  1178.     if((*stat_f)(file, &sbuf) < 0){
  1179.     switch(errno){
  1180.       case ENOENT :                /* File not found */
  1181.         return(FIOFNF);
  1182. #ifdef    ENAMETOOLONG
  1183.       case ENAMETOOLONG :            /* Name is too long */
  1184.         return(FIOLNG);
  1185. #endif
  1186.       case EACCES :                /* File not found */
  1187.         return(FIOPER);
  1188.       default:                /* Some other error */
  1189.         return(FIOERR);
  1190.     }
  1191.     }
  1192.  
  1193.     if(l)
  1194.       *l = sbuf.st_size;
  1195.  
  1196.     if((sbuf.st_mode&S_IFMT) == S_IFDIR)
  1197.       return(FIODIR);
  1198.     else if(*m == 't'){
  1199.     struct stat    sbuf2;
  1200.  
  1201.     /*
  1202.      * If it is a symbolic link pointing to a directory, treat
  1203.      * it like it is a directory, not a link.
  1204.      */
  1205.     if((sbuf.st_mode&S_IFMT) == S_IFLNK){
  1206.         if(stat(file, &sbuf2) < 0){
  1207.         switch(errno){
  1208.           case ENOENT :                /* File not found */
  1209.             return(FIOSYM);            /* call it a link */
  1210. #ifdef    ENAMETOOLONG
  1211.           case ENAMETOOLONG :            /* Name is too long */
  1212.             return(FIOLNG);
  1213. #endif
  1214.           case EACCES :                /* File not found */
  1215.             return(FIOPER);
  1216.           default:                /* Some other error */
  1217.             return(FIOERR);
  1218.         }
  1219.         }
  1220.  
  1221.         if((sbuf2.st_mode&S_IFMT) == S_IFDIR)
  1222.           return(FIODIR);
  1223.     }
  1224.  
  1225.     return(((sbuf.st_mode&S_IFMT) == S_IFLNK) ? FIOSYM : FIOSUC);
  1226.     }
  1227.  
  1228.     if(*m == 'r'){                /* read access? */
  1229.     if(*(m+1) == 'w')            /* and write access? */
  1230.       return((access(file,READ_ACCESS)==0)
  1231.          ? (access(file,WRITE_ACCESS)==0)
  1232.             ? FIOSUC
  1233.             : FIONWT
  1234.          : FIONRD);
  1235.     else if(!*(m+1))            /* just read access? */
  1236.       return((access(file,READ_ACCESS)==0) ? FIOSUC : FIONRD);
  1237.     }
  1238.     else if(*m == 'w' && !*(m+1))        /* write access? */
  1239.       return((access(file,WRITE_ACCESS)==0) ? FIOSUC : FIONWT);
  1240.     else if(*m == 'x' && !*(m+1))        /* execute access? */
  1241.       return((access(file,EXECUTE_ACCESS)==0) ? FIOSUC : FIONEX);
  1242.     return(FIOERR);                /* bad m arg */
  1243. }
  1244.  
  1245.  
  1246. /*
  1247.  * isdir - returns true if fn is a readable directory, false otherwise
  1248.  *         silent on errors (we'll let someone else notice the problem;)).
  1249.  */
  1250. isdir(fn, l)
  1251. char *fn;
  1252. long *l;
  1253. {
  1254.     struct stat sbuf;
  1255.  
  1256.     if(l)
  1257.       *l = 0;
  1258.  
  1259.     if(stat(fn, &sbuf) < 0)
  1260.       return(0);
  1261.  
  1262.     if(l)
  1263.       *l = sbuf.st_size;
  1264.     return((sbuf.st_mode&S_IFMT) == S_IFDIR);
  1265. }
  1266.  
  1267.  
  1268. #if    defined(bsd) || defined(nxt) || defined(dyn)
  1269. /*
  1270.  * getcwd - NeXT uses getwd()
  1271.  */
  1272. char *
  1273. getcwd(pth, len)
  1274. char *pth;
  1275. int   len;
  1276. {
  1277.     extern char *getwd();
  1278.  
  1279.     return(getwd(pth));
  1280. }
  1281. #endif
  1282.  
  1283.  
  1284. /*
  1285.  * gethomedir - returns the users home directory
  1286.  *              Note: home is malloc'd for life of pico
  1287.  */
  1288. char *
  1289. gethomedir(l)
  1290. int *l;
  1291. {
  1292.     static char *home = NULL;
  1293.     static short hlen = 0;
  1294.  
  1295.     if(home == NULL){
  1296.     char buf[NLINE];
  1297.     strcpy(buf, "~");
  1298.     fixpath(buf, NLINE);        /* let fixpath do the work! */
  1299.     hlen = strlen(buf);
  1300.     if((home = (char *)malloc((hlen + 1) * sizeof(char))) == NULL){
  1301.         emlwrite("Problem allocating space for home dir", NULL);
  1302.         return(0);
  1303.     }
  1304.  
  1305.     strcpy(home, buf);
  1306.     }
  1307.  
  1308.     if(l)
  1309.       *l = hlen;
  1310.  
  1311.     return(home);
  1312. }
  1313.  
  1314.  
  1315. /*
  1316.  * homeless - returns true if given file does not reside in the current
  1317.  *            user's home directory tree. 
  1318.  */
  1319. homeless(f)
  1320. char *f;
  1321. {
  1322.     char *home;
  1323.     int   len;
  1324.  
  1325.     home = gethomedir(&len);
  1326.     return(strncmp(home, f, len));
  1327. }
  1328.  
  1329.  
  1330.  
  1331. /*
  1332.  * errstr - return system error string corresponding to given errno
  1333.  *          Note: strerror() is not provided on all systems, so it's 
  1334.  *          done here once and for all.
  1335.  */
  1336. char *
  1337. errstr(err)
  1338. int err;
  1339. {
  1340. #if !defined(neb) && !defined(BSDI2)
  1341.     extern char *sys_errlist[];
  1342. #endif
  1343.     extern int  sys_nerr;
  1344.  
  1345.     return((err >= 0 && err < sys_nerr) ? sys_errlist[err] : NULL);
  1346. }
  1347.  
  1348.  
  1349.  
  1350. /*
  1351.  * getfnames - return all file names in the given directory in a single 
  1352.  *             malloc'd string.  n contains the number of names
  1353.  */
  1354. char *
  1355. getfnames(dn, pat, n, e)
  1356. char *dn, *pat, *e;
  1357. int  *n;
  1358. {
  1359.     long           l;
  1360.     char          *names, *np, *p;
  1361.     struct stat    sbuf;
  1362. #if    defined(ct)
  1363.     FILE          *dirp;
  1364.     char           fn[DIRSIZ+1];
  1365. #else
  1366.     DIR           *dirp;            /* opened directory */
  1367. #endif
  1368. #if    defined(POSIX) || defined(aix) || defined(COHERENT) || defined(isc) || defined(sv3) || defined(asv)
  1369.     struct dirent *dp;
  1370. #else
  1371.     struct direct *dp;
  1372. #endif
  1373.  
  1374.     *n = 0;
  1375.  
  1376.     if(stat(dn, &sbuf) < 0){
  1377.     switch(errno){
  1378.       case ENOENT :                /* File not found */
  1379.         if(e)
  1380.           sprintf(e, "\007File not found: \"%s\"", dn);
  1381.  
  1382.         break;
  1383. #ifdef    ENAMETOOLONG
  1384.       case ENAMETOOLONG :            /* Name is too long */
  1385.         if(e)
  1386.           sprintf(e, "\007File name too long: \"%s\"", dn);
  1387.  
  1388.         break;
  1389. #endif
  1390.       default:                /* Some other error */
  1391.         if(e)
  1392.           sprintf(e, "\007Error getting file info: \"%s\"", dn);
  1393.  
  1394.         break;
  1395.     }
  1396.     return(NULL);
  1397.     } 
  1398.     else{
  1399.     l = sbuf.st_size;
  1400.     if((sbuf.st_mode&S_IFMT) != S_IFDIR){
  1401.         if(e)
  1402.           sprintf(e, "\007Not a directory: \"%s\"", dn);
  1403.  
  1404.         return(NULL);
  1405.     }
  1406.     }
  1407.  
  1408.     if((names=(char *)malloc(sizeof(char)*l)) == NULL){
  1409.     if(e)
  1410.       sprintf(e, "\007Can't malloc space for file names", NULL);
  1411.  
  1412.     return(NULL);
  1413.     }
  1414.  
  1415.     errno = 0;
  1416.     if((dirp=opendir(dn)) == NULL){
  1417.     if(e)
  1418.       sprintf(e, "\007Can't open \"%s\": %s", dn, errstr(errno));
  1419.  
  1420.     free((char *)names);
  1421.     return(NULL);
  1422.     }
  1423.  
  1424.     np = names;
  1425.  
  1426. #if    defined(ct)
  1427.     while(fread(&dp, sizeof(struct direct), 1, dirp) > 0) {
  1428.     /* skip empty slots with inode of 0 */
  1429.     if(dp.d_ino == 0)
  1430.          continue;
  1431.     (*n)++;                     /* count the number of active slots */
  1432.     (void)strncpy(fn, dp.d_name, DIRSIZ);
  1433.     fn[14] = '\0';
  1434.     p = fn;
  1435.     while((*np++ = *p++) != '\0')
  1436.       ;
  1437.     }
  1438. #else
  1439.     while((dp = readdir(dirp)) != NULL)
  1440.       if(!pat || !*pat || !strncmp(dp->d_name, pat, strlen(pat))){
  1441.       (*n)++;
  1442.       p = dp->d_name;
  1443.       while(*np++ = *p++)
  1444.         ;
  1445.       }
  1446. #endif
  1447.  
  1448.     closedir(dirp);                    /* shut down */
  1449.     return(names);
  1450. }
  1451.  
  1452.  
  1453. /*
  1454.  * fioperr - given the error number and file name, display error
  1455.  */
  1456. void
  1457. fioperr(e, f)
  1458. int  e;
  1459. char *f;
  1460. {
  1461.     switch(e){
  1462.       case FIOFNF:                /* File not found */
  1463.     emlwrite("\007File \"%s\" not found", f);
  1464.     break;
  1465.       case FIOEOF:                /* end of file */
  1466.     emlwrite("\007End of file \"%s\" reached", f);
  1467.     break;
  1468.       case FIOLNG:                /* name too long */
  1469.     emlwrite("\007File name \"%s\" too long", f);
  1470.     break;
  1471.       case FIODIR:                /* file is a directory */
  1472.     emlwrite("\007File \"%s\" is a directory", f);
  1473.     break;
  1474.       case FIONWT:
  1475.     emlwrite("\007Write permission denied: %s", f);
  1476.     break;
  1477.       case FIONRD:
  1478.     emlwrite("\007Read permission denied: %s", f);
  1479.     break;
  1480.       case FIOPER:
  1481.     emlwrite("\007Permission denied: %s", f);
  1482.     break;
  1483.       case FIONEX:
  1484.     emlwrite("\007Execute permission denied: %s", f);
  1485.     break;
  1486.       default:
  1487.     emlwrite("\007File I/O error: %s", f);
  1488.     }
  1489. }
  1490.  
  1491.  
  1492.  
  1493. /*
  1494.  * pfnexpand - pico's function to expand the given file name if there is 
  1495.  *           a leading '~'
  1496.  */
  1497. char *pfnexpand(fn, len)
  1498. char *fn;
  1499. int  len;
  1500. {
  1501.     struct passwd *pw;
  1502.     register char *x, *y, *z;
  1503.     char *home = NULL;
  1504.     char name[20];
  1505.     
  1506.     if(*fn == '~') {
  1507.         for(x = fn+1, y = name; *x != '/' && *x != '\0'; *y++ = *x++)
  1508.       ;
  1509.  
  1510.         *y = '\0';
  1511.         if(x == fn + 1){            /* ~/ */
  1512.         if (!(home = (char *) getenv("HOME")))
  1513.           if (pw = getpwuid(geteuid()))
  1514.         home = pw->pw_dir;
  1515.     }
  1516.     else if(*name){                /* ~username/ */
  1517.         if(pw = getpwnam(name))
  1518.           home = pw->pw_dir;
  1519.     }
  1520.  
  1521.         if(!home || (strlen(home) + strlen(fn) > len))
  1522.       return(NULL);
  1523.  
  1524.     /* make room for expanded path */
  1525.     for(z = x + strlen(x), y = fn + strlen(x) + strlen(home);
  1526.         z >= x;
  1527.         *y-- = *z--)
  1528.       ;
  1529.  
  1530.     /* and insert the expanded address */
  1531.     for(x = fn, y = home; *y != '\0'; *x++ = *y++)
  1532.       ;
  1533.     }
  1534.     return(fn);
  1535. }
  1536.  
  1537.  
  1538.  
  1539. /*
  1540.  * fixpath - make the given pathname into an absolute path
  1541.  */
  1542. fixpath(name, len)
  1543. char *name;
  1544. int  len;
  1545. {
  1546.     register char *shft;
  1547.  
  1548.     /* filenames relative to ~ */
  1549.     if(!((name[0] == '/')
  1550.           || (name[0] == '.'
  1551.               && (name[1] == '/' || (name[1] == '.' && name[2] == '/'))))){
  1552.     if(Pmaster && !(gmode&MDCURDIR)
  1553.                    && (*name != '~' && strlen(name)+2 <= len)){
  1554.  
  1555.         if(gmode&MDTREE && strlen(name)+strlen(opertree)+1 <= len){
  1556.         int i, off = strlen(opertree);
  1557.  
  1558.         for(shft = strchr(name, '\0'); shft >= name; shft--)
  1559.           shft[off+1] = *shft;
  1560.  
  1561.         strncpy(name, opertree, off);
  1562.         name[off] = '/';
  1563.         }
  1564.         else{
  1565.         for(shft = strchr(name, '\0'); shft >= name; shft--)
  1566.           shft[2] = *shft;
  1567.  
  1568.         name[0] = '~';
  1569.         name[1] = '/';
  1570.         }
  1571.     }
  1572.  
  1573.     pfnexpand(name, len);
  1574.     }
  1575. }
  1576.  
  1577.  
  1578. /*
  1579.  * compresspath - given a base path and an additional directory, collapse
  1580.  *                ".." and "." elements and return absolute path (appending
  1581.  *                base if necessary).  
  1582.  *
  1583.  *                returns  1 if OK, 
  1584.  *                         0 if there's a problem
  1585.  *                         new path, by side effect, if things went OK
  1586.  */
  1587. compresspath(base, path, len)
  1588. char *base, *path;
  1589. int  len;
  1590. {
  1591.     register int i;
  1592.     int  depth = 0;
  1593.     char *p;
  1594.     char *stack[32];
  1595.     char  pathbuf[NLINE];
  1596.  
  1597. #define PUSHD(X)  (stack[depth++] = X)
  1598. #define POPD()    ((depth > 0) ? stack[--depth] : "")
  1599.  
  1600.     if(*path == '~'){
  1601.     fixpath(path, len);
  1602.     strcpy(pathbuf, path);
  1603.     }
  1604.     else if(*path != C_FILESEP)
  1605.       sprintf(pathbuf, "%s%c%s", base, C_FILESEP, path);
  1606.     else
  1607.       strcpy(pathbuf, path);
  1608.  
  1609.     p = &pathbuf[0];
  1610.     for(i=0; pathbuf[i] != '\0'; i++){        /* pass thru path name */
  1611.     if(pathbuf[i] == '/'){
  1612.         if(p != pathbuf)
  1613.           PUSHD(p);                /* push dir entry */
  1614.  
  1615.         p = &pathbuf[i+1];            /* advance p */
  1616.         pathbuf[i] = '\0';            /* cap old p off */
  1617.         continue;
  1618.     }
  1619.  
  1620.     if(pathbuf[i] == '.'){            /* special cases! */
  1621.         if(pathbuf[i+1] == '.'         /* parent */
  1622.            && (pathbuf[i+2] == '/' || pathbuf[i+2] == '\0')){
  1623.         if(!strcmp(POPD(), ""))        /* bad news! */
  1624.           return(0);
  1625.  
  1626.         i += 2;
  1627.         p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
  1628.         }
  1629.         else if(pathbuf[i+1] == '/' || pathbuf[i+1] == '\0'){
  1630.         i++;
  1631.         p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
  1632.         }
  1633.     }
  1634.     }
  1635.  
  1636.     if(*p != '\0')
  1637.       PUSHD(p);                    /* get last element */
  1638.  
  1639.     path[0] = '\0';
  1640.     for(i = 0; i < depth; i++){
  1641.     strcat(path, S_FILESEP);
  1642.     strcat(path, stack[i]);
  1643.     }
  1644.  
  1645.     return(1);                    /* everything's ok */
  1646. }
  1647.  
  1648.  
  1649. /*
  1650.  * tmpname - return a temporary file name in the given buffer
  1651.  */
  1652. void
  1653. tmpname(name)
  1654. char *name;
  1655. {
  1656.     sprintf(name, "/tmp/pico.%d", getpid());    /* tmp file name */
  1657. }
  1658.  
  1659.  
  1660. /*
  1661.  * Take a file name, and from it
  1662.  * fabricate a buffer name. This routine knows
  1663.  * about the syntax of file names on the target system.
  1664.  * I suppose that this information could be put in
  1665.  * a better place than a line of code.
  1666.  */
  1667. void
  1668. makename(bname, fname)
  1669. char    bname[];
  1670. char    fname[];
  1671. {
  1672.     register char   *cp1;
  1673.     register char   *cp2;
  1674.  
  1675.     cp1 = &fname[0];
  1676.     while (*cp1 != 0)
  1677.       ++cp1;
  1678.  
  1679.     while (cp1!=&fname[0] && cp1[-1]!='/')
  1680.       --cp1;
  1681.  
  1682.     cp2 = &bname[0];
  1683.     while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
  1684.       *cp2++ = *cp1++;
  1685.  
  1686.     *cp2 = 0;
  1687. }
  1688.  
  1689.  
  1690. /*
  1691.  * copy - copy contents of file 'a' into a file named 'b'.  Return error
  1692.  *        if either isn't accessible or is a directory
  1693.  */
  1694. copy(a, b)
  1695. char *a, *b;
  1696. {
  1697.     int    in, out, n, rv = 0;
  1698.     char   *cb;
  1699.     struct stat tsb, fsb;
  1700.     extern int  errno;
  1701.  
  1702.     if(stat(a, &fsb) < 0){        /* get source file info */
  1703.     emlwrite("Can't Copy: %s", errstr(errno));
  1704.     return(-1);
  1705.     }
  1706.  
  1707.     if(!(fsb.st_mode&S_IREAD)){        /* can we read it? */
  1708.     emlwrite("\007Read permission denied: %s", a);
  1709.     return(-1);
  1710.     }
  1711.  
  1712.     if((fsb.st_mode&S_IFMT) == S_IFDIR){ /* is it a directory? */
  1713.     emlwrite("\007Can't copy: %s is a directory", a);
  1714.     return(-1);
  1715.     }
  1716.  
  1717.     if(stat(b, &tsb) < 0){        /* get dest file's mode */
  1718.     switch(errno){
  1719.       case ENOENT:
  1720.         break;            /* these are OK */
  1721.       default:
  1722.         emlwrite("\007Can't Copy: %s", errstr(errno));
  1723.         return(-1);
  1724.     }
  1725.     }
  1726.     else{
  1727.     if(!(tsb.st_mode&S_IWRITE)){    /* can we write it? */
  1728.         emlwrite("\007Write permission denied: %s", b);
  1729.         return(-1);
  1730.     }
  1731.  
  1732.     if((tsb.st_mode&S_IFMT) == S_IFDIR){    /* is it directory? */
  1733.         emlwrite("\007Can't copy: %s is a directory", b);
  1734.         return(-1);
  1735.     }
  1736.  
  1737.     if(fsb.st_dev == tsb.st_dev && fsb.st_ino == tsb.st_ino){
  1738.         emlwrite("\007Identical files.  File not copied", NULL);
  1739.         return(-1);
  1740.     }
  1741.     }
  1742.  
  1743.     if((in = open(a, O_RDONLY)) < 0){
  1744.     emlwrite("Copy Failed: %s", errstr(errno));
  1745.     return(-1);
  1746.     }
  1747.  
  1748.     if((out=creat(b, fsb.st_mode&0xfff)) < 0){
  1749.     emlwrite("Can't Copy: %s", errstr(errno));
  1750.     close(in);
  1751.     return(-1);
  1752.     }
  1753.  
  1754.     if((cb = (char *)malloc(NLINE*sizeof(char))) == NULL){
  1755.     emlwrite("Can't allocate space for copy buffer!", NULL);
  1756.     close(in);
  1757.     close(out);
  1758.     return(-1);
  1759.     }
  1760.  
  1761.     while(1){                /* do the copy */
  1762.     if((n = read(in, cb, NLINE)) < 0){
  1763.         emlwrite("Can't Read Copy: %s", errstr(errno));
  1764.         rv = -1;
  1765.         break;            /* get out now */
  1766.     }
  1767.  
  1768.     if(n == 0)            /* done! */
  1769.       break;
  1770.  
  1771.     if(write(out, cb, n) != n){
  1772.         emlwrite("Can't Write Copy: %s", errstr(errno));
  1773.         rv = -1;
  1774.         break;
  1775.     }
  1776.     }
  1777.  
  1778.     free(cb);
  1779.     close(in);
  1780.     close(out);
  1781.     return(rv);
  1782. }
  1783.  
  1784.  
  1785. /*
  1786.  * Open a file for writing. Return TRUE if all is well, and FALSE on error
  1787.  * (cannot create).
  1788.  */
  1789. ffwopen(fn)
  1790. char    *fn;
  1791. {
  1792.     extern FILE *ffp;
  1793.  
  1794.     if ((ffp=fopen(fn, "w")) == NULL) {
  1795.         emlwrite("Cannot open file for writing", NULL);
  1796.         return (FIOERR);
  1797.     }
  1798.  
  1799.     return (FIOSUC);
  1800. }
  1801.  
  1802.  
  1803. /*
  1804.  * Close a file. Should look at the status in all systems.
  1805.  */
  1806. ffclose()
  1807. {
  1808.     extern FILE *ffp;
  1809.  
  1810.     fflush(ffp);
  1811.     if (fclose(ffp) == EOF) {
  1812.         emlwrite("\007Error closing file: %s", errstr(errno));
  1813.         return(FIOERR);
  1814.     }
  1815.  
  1816.     return(FIOSUC);
  1817. }
  1818.  
  1819.  
  1820. #if defined(asv) || defined(sco)
  1821.  
  1822. /*
  1823.  *    Tim Rice    tim@trr.metro.net    Mon Jun  3 16:57:26 PDT 1996
  1824.  *
  1825.  *    a quick and dirty trancate()
  1826.  *    Altos System V (5.3.1) does not have one
  1827.  *    neither does SCO Open Server Enterprise 3.0
  1828.  *
  1829.  */
  1830.  
  1831. truncate(fn, size)
  1832. char    *fn ;
  1833. long size ;
  1834. {
  1835.     int    fdes;
  1836.     int    rc = -1 ;
  1837.  
  1838.     if((fdes = open(fn, O_RDWR)) != -1)
  1839.     {
  1840.         rc = chsize(fdes, size) ;
  1841.  
  1842.         if(close(fdes))
  1843.             return(-1) ;
  1844.     }
  1845.     return(rc) ;
  1846. }
  1847.  
  1848. #endif /* defined(asv) || defined(sco) */
  1849.  
  1850.  
  1851. /*
  1852.  * ffelbowroom - make sure the destination's got enough room to receive
  1853.  *         what we're about to write...
  1854.  */
  1855. ffelbowroom(fn)
  1856. char    *fn;
  1857. {
  1858.     register LINE *lp;
  1859.     register long  n;
  1860.     struct   stat  sbuf;
  1861.     FILE    *fp;
  1862.     int         x, vapor = FALSE;
  1863.     char    *s, *err_str = NULL;
  1864. #define    EXTEND_BLOCK    1024
  1865.  
  1866.     /* Figure out how much room do we need */
  1867.     /* first, what's total */
  1868.     for(n=0L, lp=lforw(curbp->b_linep); lp != curbp->b_linep; lp=lforw(lp))
  1869.       n += llength(lp) + 1;
  1870.  
  1871.     if(stat(fn, &sbuf) < 0){
  1872.     if(errno == ENOENT)
  1873.       vapor = TRUE;
  1874.     else
  1875.       return(FALSE);
  1876.     }
  1877.     else
  1878.       n -= sbuf.st_size;
  1879.  
  1880.     if(n > 0L){            /* must be growing file, extend it */
  1881.     if(s = (char *) malloc(EXTEND_BLOCK * sizeof(char))){
  1882.         memset(s, 0, EXTEND_BLOCK);
  1883.         if(fp = fopen(fn, "a")){
  1884.         for( ; n > 0; n -= EXTEND_BLOCK){
  1885.             x = (n < EXTEND_BLOCK) ? EXTEND_BLOCK - n : EXTEND_BLOCK;
  1886.             if(fwrite(s, sizeof(char), x, fp) != x){
  1887.             err_str = errstr(errno);
  1888.             break;
  1889.             }
  1890.         }
  1891.             
  1892.         if(fclose(fp) == EOF)
  1893.           err_str = errstr(errno);
  1894.         }
  1895.         else
  1896.           err_str = errstr(errno);
  1897.  
  1898.         free(s);
  1899.     }
  1900.     else
  1901.       err_str = "No memory to extend file";
  1902.     }
  1903.  
  1904.     if(err_str){        /* clean up */
  1905.     if(vapor)
  1906.       unlink(fn);
  1907.     else
  1908.       truncate(fn, sbuf.st_size);
  1909.  
  1910.     emlwrite("No room for file: %s", err_str);
  1911.     return(FALSE);
  1912.     }
  1913.  
  1914.     return(TRUE);
  1915. }
  1916.  
  1917.  
  1918. /*
  1919.  * P_open - run the given command in a sub-shell returning a file pointer
  1920.  *        from which to read the output
  1921.  *
  1922.  * note:
  1923.  *    For OS's other than unix, you will have to rewrite this function.
  1924.  *    Hopefully it'll be easy to exec the command into a temporary file, 
  1925.  *    and return a file pointer to that opened file or something.
  1926.  */
  1927. FILE *P_open(s)
  1928. char *s;
  1929. {
  1930.     return(popen(s, "r"));
  1931. }
  1932.  
  1933.  
  1934.  
  1935. /*
  1936.  * P_close - close the given descriptor
  1937.  *
  1938.  */
  1939. P_close(fp)
  1940. FILE *fp;
  1941. {
  1942.     return(pclose(fp));
  1943. }
  1944.  
  1945.  
  1946.  
  1947. /*
  1948.  * worthit - generic sort of test to roughly gage usefulness of using 
  1949.  *           optimized scrolling.
  1950.  *
  1951.  * note:
  1952.  *    returns the line on the screen, l, that the dot is currently on
  1953.  */
  1954. worthit(l)
  1955. int *l;
  1956. {
  1957.     int i;            /* l is current line */
  1958.     unsigned below;        /* below is avg # of ch/line under . */
  1959.  
  1960.     *l = doton(&i, &below);
  1961.     below = (i > 0) ? below/(unsigned)i : 0;
  1962.  
  1963.     return(below > 3);
  1964. }
  1965.  
  1966.  
  1967.  
  1968. /*
  1969.  * pico_new_mail - just checks mtime and atime of mail file and notifies user 
  1970.  *               if it's possible that they have new mail.
  1971.  */
  1972. pico_new_mail()
  1973. {
  1974.     int ret = 0;
  1975.     static time_t lastchk = 0;
  1976.     struct stat sbuf;
  1977.     char   inbox[256], *p;
  1978.  
  1979.     if(p = (char *)getenv("MAIL"))
  1980.       sprintf(inbox, p);
  1981.     else
  1982.       sprintf(inbox,"%s/%s", MAILDIR, getlogin());
  1983.  
  1984.     if(stat(inbox, &sbuf) == 0){
  1985.     ret = sbuf.st_atime <= sbuf.st_mtime &&
  1986.       (lastchk < sbuf.st_mtime && lastchk < sbuf.st_atime);
  1987.     lastchk = sbuf.st_mtime;
  1988.     return(ret);
  1989.     }
  1990.     else
  1991.       return(ret);
  1992. }
  1993.  
  1994.  
  1995.  
  1996. /*
  1997.  * time_to_check - checks the current time against the last time called 
  1998.  *                 and returns true if the elapsed time is > timeout
  1999.  */
  2000. time_to_check()
  2001. {
  2002.     static time_t lasttime = 0L;
  2003.  
  2004.     if(!timeout)
  2005.       return(FALSE);
  2006.  
  2007.     if(time((time_t *) 0) - lasttime > (time_t)timeout){
  2008.     lasttime = time((time_t *) 0);
  2009.     return(TRUE);
  2010.     }
  2011.     else
  2012.       return(FALSE);
  2013. }
  2014.  
  2015.  
  2016. /*
  2017.  * sstrcasecmp - compare two pointers to strings case independently
  2018.  */
  2019. sstrcasecmp(s1, s2)
  2020. QcompType *s1, *s2;
  2021. {
  2022.     register char *a, *b;
  2023.  
  2024.     a = *(char **)s1;
  2025.     b = *(char **)s2;
  2026.     while(toupper((unsigned char)(*a)) == toupper((unsigned char)(*b++)))
  2027.     if(*a++ == '\0')
  2028.       return(0);
  2029.  
  2030.     return(toupper((unsigned char)(*a)) - toupper((unsigned char)(*--b)));
  2031. }
  2032.  
  2033.  
  2034. /*
  2035.  * chkptinit -- initialize anything we need to support composer
  2036.  *        checkpointing
  2037.  */
  2038. chkptinit(file, n)
  2039.     char *file;
  2040.     int   n;
  2041. {
  2042.     unsigned pid;
  2043.     char    *chp;
  2044.  
  2045.     if(!file[0]){
  2046.     long gmode_save = gmode;
  2047.  
  2048.     if(gmode&MDCURDIR)
  2049.       gmode &= ~MDCURDIR;  /* so fixpath will use home dir */
  2050.  
  2051.     strcpy(file, "#picoXXXXX#");
  2052.     fixpath(file, NLINE);
  2053.     gmode = gmode_save;
  2054.     }
  2055.     else{
  2056.     int l = strlen(file);
  2057.  
  2058.     if(file[l-1] != '/'){
  2059.         file[l++] = '/';
  2060.         file[l]   = '\0';
  2061.     }
  2062.  
  2063.     strcpy(file + l, "#picoXXXXX#");
  2064.     }
  2065.  
  2066.     pid = (unsigned)getpid();
  2067.     for(chp = file+strlen(file) - 2; *chp == 'X'; chp--){
  2068.     *chp = (pid % 10) + '0';
  2069.     pid /= 10;
  2070.     }
  2071.  
  2072.     unlink(file);
  2073. }
  2074.  
  2075.  
  2076. #ifdef    TIOCGWINSZ
  2077. /*
  2078.  * winch_handler - handle window change signal
  2079.  */
  2080. SIGTYPE winch_handler()
  2081. {
  2082.     signal(SIGWINCH, winch_handler);
  2083.     ttresize();
  2084. }
  2085. #endif    /* TIOCGWINSZ */
  2086.  
  2087.  
  2088. #ifdef    SIGCHLD
  2089. /*
  2090.  * child_handler - handle child status change signal
  2091.  */
  2092. SIGTYPE child_handler()
  2093. {
  2094.     pico_child_done = 1;
  2095.     if(pico_child_jmp_ok)
  2096.       longjmp(pico_child_state, 1);
  2097. }
  2098. #endif    /* SIGCHLD */
  2099.  
  2100.  
  2101. #if    defined(sv3) || defined(ct)
  2102. /* Placed by rll to handle the rename function not found in AT&T */
  2103. rename(oldname, newname)
  2104.     char *oldname;
  2105.     char *newname;
  2106. {
  2107.     int rtn;
  2108.  
  2109.     if ((rtn = link(oldname, newname)) != 0) {
  2110.     perror("Was not able to rename file.");
  2111.     return(rtn);
  2112.     }
  2113.  
  2114.     if ((rtn = unlink(oldname)) != 0)
  2115.       perror("Was not able to unlink file.");
  2116.  
  2117.     return(rtn);
  2118. }
  2119. #endif
  2120.  
  2121.  
  2122. #ifdef    MOUSE
  2123.  
  2124. /* 
  2125.  * init_mouse - check for xterm and initialize mouse tracking if present...
  2126.  */
  2127. init_mouse()
  2128. {
  2129.     if(mexist)
  2130.       return(TRUE);
  2131.  
  2132.     if(getenv("DISPLAY")){
  2133.     mouseon();
  2134.     return(mexist = TRUE);
  2135.     }
  2136.     else
  2137.       return(FALSE);
  2138. }
  2139.  
  2140.  
  2141. /* 
  2142.  * end_mouse - clear xterm mouse tracking if present...
  2143.  */
  2144. void
  2145. end_mouse()
  2146. {
  2147.     if(mexist){
  2148.     mexist = 0;            /* just see if it exists here. */
  2149.     mouseoff();
  2150.     }
  2151. }
  2152.  
  2153.  
  2154. /*
  2155.  * mouseexist - function to let outsiders know if mouse is turned on
  2156.  *              or not.
  2157.  */
  2158. mouseexist()
  2159. {
  2160.     return(mexist);
  2161. }
  2162.  
  2163.  
  2164. /*
  2165.  * mouseon - call made available for programs calling pico to turn ON the
  2166.  *           mouse cursor.
  2167.  */
  2168. void
  2169. mouseon()
  2170. {
  2171.     fputs(XTERM_MOUSE_ON, stdout);
  2172. }
  2173.  
  2174.  
  2175. /*
  2176.  * mouseon - call made available for programs calling pico to turn OFF the
  2177.  *           mouse cursor.
  2178.  */
  2179. void
  2180. mouseoff()
  2181. {
  2182.     fputs(XTERM_MOUSE_OFF, stdout);
  2183. }
  2184.  
  2185.  
  2186. /* 
  2187.  * checkmouse - look for mouse events in key menu and return 
  2188.  *              appropriate value.
  2189.  */
  2190. int
  2191. checkmouse(ch, down, mcol, mrow)
  2192. unsigned *ch;
  2193. int      down, mcol, mrow;
  2194. {
  2195.     static int oindex;
  2196.     register int k;            /* current bit/button of mouse */
  2197.     int i = 0, rv = 0;
  2198.     MENUITEM *mp;
  2199.  
  2200.     if(!mexist || mcol < 0 || mrow < 0)
  2201.       return(FALSE);
  2202.  
  2203.     if(down)            /* button down */
  2204.       oindex = -1;
  2205.  
  2206.     for(mp = mfunc; mp; mp = mp->next)
  2207.       if(mp->action && M_ACTIVE(mrow, mcol, mp))
  2208.     break;
  2209.  
  2210.     if(mp){
  2211.     unsigned long r;
  2212.  
  2213.     r = (*mp->action)(down ? M_EVENT_DOWN : M_EVENT_UP,
  2214.               mrow, mcol, M_BUTTON_LEFT, 0);
  2215.     if(r & 0xffff){
  2216.         *ch = (unsigned)((r>>16)&0xffff);
  2217.         rv  = TRUE;
  2218.     }
  2219.     }
  2220.     else{
  2221.     while(1){            /* see if we understand event */
  2222.         if(i >= 12){
  2223.         i = -1;
  2224.         break;
  2225.         }
  2226.  
  2227.         if(M_ACTIVE(mrow, mcol, &menuitems[i]))
  2228.           break;
  2229.  
  2230.         i++;
  2231.     }
  2232.  
  2233.     if(down){            /* button down */
  2234.         oindex = i;            /* remember where */
  2235.         if(i != -1
  2236.            && menuitems[i].label_hiliter != NULL
  2237.            && menuitems[i].val != mnoop)  /* invert label */
  2238.           (*menuitems[i].label_hiliter)(1, &menuitems[i]);
  2239.     }
  2240.     else{                /* button up */
  2241.         if(oindex != -1){
  2242.         if(i == oindex){
  2243.             *ch = menuitems[i].val;
  2244.             rv = TRUE;
  2245.         }
  2246.         }
  2247.     }
  2248.     }
  2249.  
  2250.     /* restore label */
  2251.     if(!down
  2252.        && oindex != -1
  2253.        && menuitems[oindex].label_hiliter != NULL
  2254.        && menuitems[oindex].val != mnoop)
  2255.       (*menuitems[oindex].label_hiliter)(0, &menuitems[oindex]);
  2256.  
  2257.     return(rv);
  2258. }
  2259.  
  2260.  
  2261. /*
  2262.  * invert_label - highlight the label of the given menu item.
  2263.  */
  2264. void
  2265. invert_label(state, m)
  2266. int state;
  2267. MENUITEM *m;
  2268. {
  2269.     unsigned i, j;
  2270.     int   r, c, p, oldr, oldc, col_offset;
  2271.     char *lp;
  2272.  
  2273.     /*
  2274.      * Leave the command name bold
  2275.      */
  2276.     col_offset = (state || !(lp=strchr(m->label, ' '))) ? 0 : (lp - m->label);
  2277.     movecursor((int)m->tl.r, (int)m->tl.c + col_offset);
  2278.     (*term.t_rev)(state);
  2279.  
  2280.     for(i = m->tl.r; i <= m->br.r; i++)
  2281.       for(j = m->tl.c + col_offset; j <= m->br.c; j++)
  2282.     if(i == m->lbl.r && j == m->lbl.c + col_offset && m->label){
  2283.         lp = m->label + col_offset;        /* show label?? */
  2284.         while(*lp && j++ < m->br.c)
  2285.           putc(*lp++, stdout);
  2286.  
  2287.         continue;
  2288.     }
  2289.     else
  2290.       putc(' ', stdout);
  2291.  
  2292.     if(state)
  2293.       (*term.t_rev)(0);  /* turn inverse back off */
  2294. }
  2295. #endif    /* MOUSE */
  2296.